#include "core/GraphicEngine.hh"

#include "common/stl.h"
#include "tools/constants.hh"
#include "tools/Matrix.hh"


GraphicEngine::GraphicEngine() :
	_fov (0),
	_aspectRatio (0),
	_zNear (0),
	_zFar (0),
	_zRange (0),
	_distance (0)
{
}

GraphicEngine::GraphicEngine(float fov, float ratio, float near, float far) :
	_fov (fov),
	_aspectRatio (ratio),
	_zNear (near),
	_zFar (far),
	_zRange (far - near),
	_distance (1.0f / tanf(MATHS_PI_DIV_180 * (_fov / 2.0f))),
	_perspective ()
{
	calcPerspective(fov, ratio, near, far);
}

GraphicEngine::~GraphicEngine()
{
}


// getters / setters
Matrix GraphicEngine::getMVP()
{
	// TODO could optimize, store this result, do not recalculate for each frame / object
	return _perspective * _model * _camera;
}

Matrix GraphicEngine::getCamera()
{
	return _camera;
}

void GraphicEngine::setModel(Matrix model)
{
	_model = model;
}

void GraphicEngine::setCamera(Matrix c)
{
	_camera = c;
}


void GraphicEngine::setCameraPosition(float x, float y, float z)
{
	_camera[0][3] = x;
	_camera[1][3] = y;
	_camera[2][3] = z;
}

// ----- Camera Motion

// 0 -1 0: move down
void GraphicEngine::moveCamera(float x, float y, float z)
{
	_camera[0][3] += x;
	_camera[1][3] += y;
	_camera[2][3] += z;

	// update _camera's Target to make sure we're not aiming the same point anymore
	if (x > FLT_EPSILON || x < FLT_EPSILON)
		_camera[0][2] += x;
}


// rotation over y axe
void GraphicEngine::turnCamera(float x, float y, float z)
{
	_camera[0][2] += x;
	_camera[1][2] += y;
	_camera[2][2] += z;
}


void GraphicEngine::tiltCamera(float x, float y, float z)
{
	_camera[0][1] += x;
	_camera[1][1] += y;
	_camera[2][1] += z;
}


void GraphicEngine::calcPerspective(float fov, float ratio, float zNear, float zFar)
{
	_fov = fov;
	_aspectRatio = ratio;

	_zNear	= zNear;
	_zFar	= zFar;
	_zRange	= zNear - zFar;

	_distance = 1.0f / tanf(MATHS_PI_DIV_180 * (_fov / 2.0f));
}


void GraphicEngine::updatePerspective()
{
	_perspective.loadIdentity();

	_perspective[0][0] = _distance / _aspectRatio;
	_perspective[1][1] = _distance;
	_perspective[2][2] = (-_zNear -_zFar) / _zRange;
	_perspective[2][3] = 2.0f * _zFar * _zNear / _zRange;
	_perspective[3][2] = 1.0f;
	_perspective[3][3] = 0.0f;
}
